/**
 * websocket工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import { getToken } from '../auth';
import { baseURL } from '@/config/global';

/**
 * 自动重连的 WebSocket
 */
export class WebSocker {
  private opened: boolean;

  private ws: WebSocket;

  private url: Function;

  private readonly interval: number;

  private readonly openListeners: Function[];

  private readonly closeListeners: Function[];

  private readonly errorListeners: Function[];

  private readonly messageListeners: Function[];

  /**
   * 路径，是否立即创建链接
   * @param url 路径
   * @param interval 重连间隔
   * @param immediate 是否立即连接
   */
  constructor(url: string | Function, interval = 30, immediate = true) {
    if (!('WebSocket' in window)) {
      console.error('您的浏览器不支持WebSocket');
    }
    this.url = typeof url === 'string' ? (() => url) : url;
    this.interval = interval * 1000;
    this.openListeners = [];
    this.closeListeners = [];
    this.errorListeners = [];
    this.messageListeners = [];
    this.opened = false;
    if (immediate) {
      this.open();
    }
  }

  /**
   * 连接
   */
  private async connection() {
    if (!this.opened || this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING) {
      return;
    }
    try {
      this.ws = new WebSocket(this.url());
      this.ws.onopen = (e) => {
        this.openListeners.forEach((x) => x(e));
      };
      this.ws.onclose = (e) => {
        this.closeListeners.forEach((x) => x(e));
        if (this.interval > 1000 && this.opened) {
          // @ts-ignore
          clearTimeout(this.timer);
          // @ts-ignore 自动重连
          this.timer = setTimeout(() => this.connection(), this.interval);
        }
      };
      this.ws.onerror = (e) => {
        this.errorListeners.forEach((x) => x(e));
      };
      this.ws.onmessage = (e) => {
        this.messageListeners?.forEach((x) => x(e.data, e));
      };
    } catch (e) {
      console.error('WebSocket连接失败', e);
    }
  }

  /**
   * 重新连接
   * @param url
   */
  reconnection(url: string | Function) {
    this.url = typeof url === 'string' ? () => url : url;
    this.opened = true;
    this.ws?.close();
    this.connection();
  }

  /**
   * 打开连接
   */
  open() {
    this.opened = true;
    this.connection();
  }

  /**
   * 关闭
   */
  close() {
    this.opened = false;
    this.ws?.close();
  }

  /**
   * 是否开启
   */
  isOpen() {
    return this.opened;
  }

  /**
   * 发送消息
   * @param data
   * @param topic 消息主题
   */
  send(data: any) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      if (typeof data === 'object') {
        data = JSON.stringify(data);
      }
      this.ws.send(data);
    }
  }

  /**
   * 监听连接成功
   * @param listener
   */
  onopen(listener: Function) {
    if (!this.openListeners.includes(listener)) {
      this.openListeners.push(listener);
    }
  }

  /**
   * 监听连接关闭
   * @param listener
   */
  onclose(listener: Function) {
    if (!this.closeListeners.includes(listener)) {
      this.closeListeners.push(listener);
    }
  }

  /**
   * 监听异常
   * @param listener
   */
  onerror(listener: Function) {
    if (!this.errorListeners.includes(listener)) {
      this.errorListeners.push(listener);
    }
  }

  /**
   * 监听收到后端消息
   * @param listener 监听函数
   */
  onmessage(listener: Function) {
    if (!this.messageListeners?.includes(listener)) {
      this.messageListeners?.push(listener);
    }
  }
}

let webSocker: WebSocker = null;
const messageListenerMap: { [k: string]: Function[] } = {};
function onmessage(data, e) {
  const index = data.indexOf('::');
  if (index === -1) {
    return;
  }
  const topic = data.substring(0, index);
  data = data.substring(index + 2);
  messageListenerMap[topic]?.forEach((x) => x(data, e));
}

/**
 * 初始化 webSocket，此方法无论调用多少次都只有一个唯一的 WebSocker 实例，需要多个请使用 new WebSocker()
 */
export function initWebSocket(): WebSocker {
  if (import.meta.env.VITE_WEBSOCKET_DISABLED === 'true') {
    return null;
  }
  if (!webSocker) {
    webSocker = new WebSocker(
      () => `ws${window.location.origin.substring(4)}${baseURL}/message/ws?token=${getToken()}`,
      30,
      false,
    );
    webSocker.onmessage(onmessage);
  }
  return webSocker;
}

/**
 * 打开websocket连接
 */
export function openWebSocket() {
  if (!webSocker?.isOpen()) {
    setTimeout(() => initWebSocket()?.open(), 2500);
  }
}

/**
 * 关闭websocket连接
 */
export function closeWebSocket() {
  webSocker?.close();
}

/**
 * 监听websocket消息
 * @param topic
 * @param listener
 */
export function onWebSocket(topic: string, listener: Function) {
  if (topic) {
    messageListenerMap[topic] ??= [];
    const list = messageListenerMap[topic];
    if (!list.includes(listener)) {
      list.push(listener);
    }
  } else {
    initWebSocket()?.onmessage(listener);
  }
}

/**
 * 发送websocket消息
 * @param topic
 * @param data
 */
export function sendWebSocket(topic: string, data: any) {
  if (typeof data === 'object') {
    data = JSON.stringify(data);
  }
  webSocker?.send(`${topic}::${data}`);
}
